1. Project Clover database Wed Dec 28 2022 16:43:51 MST
  2. Package org.joda.time

File DateTimeZone.java

 

Coverage histogram

../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

156
327
49
2
1,324
583
148
0.45
6.67
24.5
3.02

Classes

Class Line # Actions
DateTimeZone 88 323 0% 144 39
0.925572592.6%
DateTimeZone.Stub 1297 4 0% 4 0
1.0100%
 

Contributing tests

This file is covered by 2579 tests. .

Source view

1    /*
2    * Copyright 2001-2013 Stephen Colebourne
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10    * Unless required by applicable law or agreed to in writing, software
11    * distributed under the License is distributed on an "AS IS" BASIS,
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    * See the License for the specific language governing permissions and
14    * limitations under the License.
15    */
16    package org.joda.time;
17   
18    import java.io.IOException;
19    import java.io.ObjectInputStream;
20    import java.io.ObjectOutputStream;
21    import java.io.ObjectStreamException;
22    import java.io.Serializable;
23    import java.lang.ref.Reference;
24    import java.lang.ref.SoftReference;
25    import java.util.HashMap;
26    import java.util.Locale;
27    import java.util.Map;
28    import java.util.Set;
29    import java.util.TimeZone;
30   
31    import org.joda.convert.FromString;
32    import org.joda.convert.ToString;
33    import org.joda.time.chrono.BaseChronology;
34    import org.joda.time.field.FieldUtils;
35    import org.joda.time.format.DateTimeFormatter;
36    import org.joda.time.format.DateTimeFormatterBuilder;
37    import org.joda.time.format.FormatUtils;
38    import org.joda.time.tz.DefaultNameProvider;
39    import org.joda.time.tz.FixedDateTimeZone;
40    import org.joda.time.tz.NameProvider;
41    import org.joda.time.tz.Provider;
42    import org.joda.time.tz.UTCProvider;
43    import org.joda.time.tz.ZoneInfoProvider;
44   
45    /**
46    * DateTimeZone represents a time zone.
47    * <p>
48    * A time zone is a system of rules to convert time from one geographic
49    * location to another. For example, Paris, France is one hour ahead of
50    * London, England. Thus when it is 10:00 in London, it is 11:00 in Paris.
51    * <p>
52    * All time zone rules are expressed, for historical reasons, relative to
53    * Greenwich, London. Local time in Greenwich is referred to as Greenwich Mean
54    * Time (GMT). This is similar, but not precisely identical, to Universal
55    * Coordinated Time, or UTC. This library only uses the term UTC.
56    * <p>
57    * Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00
58    * in the summer. The offset -08:00 indicates that America/Los_Angeles time is
59    * obtained from UTC by adding -08:00, that is, by subtracting 8 hours.
60    * <p>
61    * The offset differs in the summer because of daylight saving time, or DST.
62    * The following definitions of time are generally used:
63    * <ul>
64    * <li>UTC - The reference time.
65    * <li>Standard Time - The local time without a daylight saving time offset.
66    * For example, in Paris, standard time is UTC+01:00.
67    * <li>Daylight Saving Time - The local time with a daylight saving time
68    * offset. This offset is typically one hour, but not always. It is typically
69    * used in most countries away from the equator. In Paris, daylight saving
70    * time is UTC+02:00.
71    * <li>Wall Time - This is what a local clock on the wall reads. This will be
72    * either Standard Time or Daylight Saving Time depending on the time of year
73    * and whether the location uses Daylight Saving Time.
74    * </ul>
75    * <p>
76    * Unlike the Java TimeZone class, DateTimeZone is immutable. It also only
77    * supports long format time zone ids. Thus EST and ECT are not accepted.
78    * However, the factory that accepts a TimeZone will attempt to convert from
79    * the old short id to a suitable long id.
80    * <p>
81    * DateTimeZone is thread-safe and immutable, and all subclasses must be as
82    * well.
83    *
84    * @author Brian S O'Neill
85    * @author Stephen Colebourne
86    * @since 1.0
87    */
 
88    public abstract class DateTimeZone implements Serializable {
89   
90    /** Serialization version. */
91    private static final long serialVersionUID = 5546345482340108586L;
92   
93    /** The time zone for Universal Coordinated Time */
94    public static final DateTimeZone UTC = new FixedDateTimeZone("UTC", "UTC", 0, 0);
95    /** Maximum offset. */
96    private static final int MAX_MILLIS = (86400 * 1000) - 1;
97   
98    /** The instance that is providing time zones. */
99    private static Provider cProvider;
100    /** The instance that is providing time zone names. */
101    private static NameProvider cNameProvider;
102    /** The set of ID strings. */
103    private static Set<String> cAvailableIDs;
104    /** The default time zone. */
105    private static volatile DateTimeZone cDefault;
106    /** A formatter for printing and parsing zones. */
107    private static DateTimeFormatter cOffsetFormatter;
108   
109    /** Cache that maps fixed offset strings to softly referenced DateTimeZones */
110    private static Map<String, SoftReference<DateTimeZone>> iFixedOffsetCache;
111   
112    /** Cache of old zone IDs to new zone IDs */
113    private static Map<String, String> cZoneIdConversion;
114   
 
115  2 toggle static {
116  2 setProvider0(null);
117  2 setNameProvider0(null);
118    }
119   
120    //-----------------------------------------------------------------------
121    /**
122    * Gets the default time zone.
123    * <p>
124    * The default time zone is derived from the system property {@code user.timezone}.
125    * If that is {@code null} or is not a valid identifier, then the value of the
126    * JDK {@code TimeZone} default is converted. If that fails, {@code UTC} is used.
127    * <p>
128    * NOTE: If the {@code java.util.TimeZone} default is updated <i>after</i> calling this
129    * method, then the change will not be picked up here.
130    *
131    * @return the default datetime zone object
132    */
 
133  11593 toggle public static DateTimeZone getDefault() {
134  11593 DateTimeZone zone = cDefault;
135  11593 if (zone == null) {
136  2 synchronized(DateTimeZone.class) {
137  2 zone = cDefault;
138  2 if (zone == null) {
139  2 DateTimeZone temp = null;
140  2 try {
141  2 try {
142  2 String id = System.getProperty("user.timezone");
143  2 if (id != null) { // null check avoids stack overflow
144  2 temp = forID(id);
145    }
146    } catch (RuntimeException ex) {
147    // ignored
148    }
149  2 if (temp == null) {
150  2 temp = forTimeZone(TimeZone.getDefault());
151    }
152    } catch (IllegalArgumentException ex) {
153    // ignored
154    }
155  2 if (temp == null) {
156  1 temp = UTC;
157    }
158  2 cDefault = zone = temp;
159    }
160    }
161    }
162  11593 return zone;
163    }
164   
165    /**
166    * Sets the default time zone.
167    * <p>
168    * NOTE: Calling this method does <i>not</i> set the {@code java.util.TimeZone} default.
169    *
170    * @param zone the default datetime zone object, must not be null
171    * @throws IllegalArgumentException if the zone is null
172    * @throws SecurityException if the application has insufficient security rights
173    */
 
174  6064 toggle public static void setDefault(DateTimeZone zone) throws SecurityException {
175  6064 SecurityManager sm = System.getSecurityManager();
176  6064 if (sm != null) {
177  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setDefault"));
178    }
179  6064 if (zone == null) {
180  1 throw new IllegalArgumentException("The datetime zone must not be null");
181    }
182  6063 synchronized(DateTimeZone.class) {
183  6063 cDefault = zone;
184    }
185    }
186   
187    //-----------------------------------------------------------------------
188    /**
189    * Gets a time zone instance for the specified time zone id.
190    * <p>
191    * The time zone id may be one of those returned by getAvailableIDs.
192    * Short ids, as accepted by {@link java.util.TimeZone}, are not accepted.
193    * All IDs must be specified in the long format.
194    * The exception is UTC, which is an acceptable id.
195    * <p>
196    * Alternatively a locale independent, fixed offset, datetime zone can
197    * be specified. The form <code>[+-]hh:mm</code> can be used.
198    *
199    * @param id the ID of the datetime zone, null means default
200    * @return the DateTimeZone object for the ID
201    * @throws IllegalArgumentException if the ID is not recognised
202    */
 
203  465 toggle @FromString
204    public static DateTimeZone forID(String id) {
205  465 if (id == null) {
206  1 return getDefault();
207    }
208  464 if (id.equals("UTC")) {
209  13 return DateTimeZone.UTC;
210    }
211  451 DateTimeZone zone = cProvider.getZone(id);
212  451 if (zone != null) {
213  406 return zone;
214    }
215  45 if (id.startsWith("+") || id.startsWith("-")) {
216  32 int offset = parseOffset(id);
217  30 if (offset == 0L) {
218  3 return DateTimeZone.UTC;
219    } else {
220  27 id = printOffset(offset);
221  27 return fixedOffsetZone(id, offset);
222    }
223    }
224  13 throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
225    }
226   
227    /**
228    * Gets a time zone instance for the specified offset to UTC in hours.
229    * This method assumes standard length hours.
230    * <p>
231    * This factory is a convenient way of constructing zones with a fixed offset.
232    *
233    * @param hoursOffset the offset in hours from UTC, from -23 to +23
234    * @return the DateTimeZone object for the offset
235    * @throws IllegalArgumentException if the offset is too large or too small
236    */
 
237  25 toggle public static DateTimeZone forOffsetHours(int hoursOffset) throws IllegalArgumentException {
238  25 return forOffsetHoursMinutes(hoursOffset, 0);
239    }
240   
241    /**
242    * Gets a time zone instance for the specified offset to UTC in hours and minutes.
243    * This method assumes 60 minutes in an hour, and standard length minutes.
244    * <p>
245    * This factory is a convenient way of constructing zones with a fixed offset.
246    * The hours value must be in the range -23 to +23.
247    * The minutes value must be in the range -59 to +59.
248    * The following combinations of sign for the hour and minute are possible:
249    * <pre>
250    * Hour Minute Example Result
251    *
252    * +ve +ve (2, 15) +02:15
253    * +ve zero (2, 0) +02:00
254    * +ve -ve (2, -15) IllegalArgumentException
255    *
256    * zero +ve (0, 15) +00:15
257    * zero zero (0, 0) +00:00
258    * zero -ve (0, -15) -00:15
259    *
260    * -ve +ve (-2, 15) -02:15
261    * -ve zero (-2, 0) -02:00
262    * -ve -ve (-2, -15) -02:15
263    * </pre>
264    * Note that in versions before 2.3, the minutes had to be zero or positive.
265    *
266    * @param hoursOffset the offset in hours from UTC, from -23 to +23
267    * @param minutesOffset the offset in minutes from UTC, from -59 to +59
268    * @return the DateTimeZone object for the offset
269    * @throws IllegalArgumentException if any value is out of range, the minutes are negative
270    * when the hours are positive, or the resulting offset exceeds +/- 23:59:59.000
271    */
 
272  42 toggle public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
273  42 if (hoursOffset == 0 && minutesOffset == 0) {
274  3 return DateTimeZone.UTC;
275    }
276  39 if (hoursOffset < -23 || hoursOffset > 23) {
277  3 throw new IllegalArgumentException("Hours out of range: " + hoursOffset);
278    }
279  36 if (minutesOffset < -59 || minutesOffset > 59) {
280  2 throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
281    }
282  34 if (hoursOffset > 0 && minutesOffset < 0) {
283  1 throw new IllegalArgumentException("Positive hours must not have negative minutes: " + minutesOffset);
284    }
285  33 int offset = 0;
286  33 try {
287  33 int hoursInMinutes = hoursOffset * 60;
288  33 if (hoursInMinutes < 0) {
289  9 minutesOffset = hoursInMinutes - Math.abs(minutesOffset);
290    } else {
291  24 minutesOffset = hoursInMinutes + minutesOffset;
292    }
293  33 offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
294    } catch (ArithmeticException ex) {
295  0 throw new IllegalArgumentException("Offset is too large");
296    }
297  33 return forOffsetMillis(offset);
298    }
299   
300    /**
301    * Gets a time zone instance for the specified offset to UTC in milliseconds.
302    *
303    * @param millisOffset the offset in millis from UTC, from -23:59:59.999 to +23:59:59.999
304    * @return the DateTimeZone object for the offset
305    */
 
306  77 toggle public static DateTimeZone forOffsetMillis(int millisOffset) {
307  77 if (millisOffset < -MAX_MILLIS || millisOffset > MAX_MILLIS) {
308  0 throw new IllegalArgumentException("Millis out of range: " + millisOffset);
309    }
310  77 String id = printOffset(millisOffset);
311  77 return fixedOffsetZone(id, millisOffset);
312    }
313   
314    /**
315    * Gets a time zone instance for a JDK TimeZone.
316    * <p>
317    * DateTimeZone only accepts a subset of the IDs from TimeZone. The
318    * excluded IDs are the short three letter form (except UTC). This
319    * method will attempt to convert between time zones created using the
320    * short IDs and the full version.
321    * <p>
322    * This method is not designed to parse time zones with rules created by
323    * applications using <code>SimpleTimeZone</code> directly.
324    *
325    * @param zone the zone to convert, null means default
326    * @return the DateTimeZone object for the zone
327    * @throws IllegalArgumentException if the zone is not recognised
328    */
 
329  83 toggle public static DateTimeZone forTimeZone(TimeZone zone) {
330  83 if (zone == null) {
331  1 return getDefault();
332    }
333  82 final String id = zone.getID();
334  82 if (id.equals("UTC")) {
335  1 return DateTimeZone.UTC;
336    }
337   
338    // Convert from old alias before consulting provider since they may differ.
339  81 DateTimeZone dtz = null;
340  81 String convId = getConvertedId(id);
341  81 if (convId != null) {
342  35 dtz = cProvider.getZone(convId);
343    }
344  81 if (dtz == null) {
345  46 dtz = cProvider.getZone(id);
346    }
347  81 if (dtz != null) {
348  71 return dtz;
349    }
350   
351    // Support GMT+/-hh:mm formats
352  10 if (convId == null) {
353  10 convId = zone.getID();
354  10 if (convId.startsWith("GMT+") || convId.startsWith("GMT-")) {
355  8 convId = convId.substring(3);
356  8 int offset = parseOffset(convId);
357  8 if (offset == 0L) {
358  3 return DateTimeZone.UTC;
359    } else {
360  5 convId = printOffset(offset);
361  5 return fixedOffsetZone(convId, offset);
362    }
363    }
364    }
365  2 throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
366    }
367   
368    //-----------------------------------------------------------------------
369    /**
370    * Gets the zone using a fixed offset amount.
371    *
372    * @param id the zone id
373    * @param offset the offset in millis
374    * @return the zone
375    */
 
376  109 toggle private static synchronized DateTimeZone fixedOffsetZone(String id, int offset) {
377  109 if (offset == 0) {
378  13 return DateTimeZone.UTC;
379    }
380  96 if (iFixedOffsetCache == null) {
381  1 iFixedOffsetCache = new HashMap<String, SoftReference<DateTimeZone>>();
382    }
383  96 DateTimeZone zone;
384  96 Reference<DateTimeZone> ref = iFixedOffsetCache.get(id);
385  96 if (ref != null) {
386  71 zone = ref.get();
387  71 if (zone != null) {
388  71 return zone;
389    }
390    }
391  25 zone = new FixedDateTimeZone(id, null, offset, offset);
392  25 iFixedOffsetCache.put(id, new SoftReference<DateTimeZone>(zone));
393  25 return zone;
394    }
395   
396    /**
397    * Gets all the available IDs supported.
398    *
399    * @return an unmodifiable Set of String IDs
400    */
 
401  5 toggle public static Set<String> getAvailableIDs() {
402  5 return cAvailableIDs;
403    }
404   
405    //-----------------------------------------------------------------------
406    /**
407    * Gets the zone provider factory.
408    * <p>
409    * The zone provider is a pluggable instance factory that supplies the
410    * actual instances of DateTimeZone.
411    *
412    * @return the provider
413    */
 
414  8 toggle public static Provider getProvider() {
415  8 return cProvider;
416    }
417   
418    /**
419    * Sets the zone provider factory.
420    * <p>
421    * The zone provider is a pluggable instance factory that supplies the
422    * actual instances of DateTimeZone.
423    *
424    * @param provider provider to use, or null for default
425    * @throws SecurityException if you do not have the permission DateTimeZone.setProvider
426    * @throws IllegalArgumentException if the provider is invalid
427    */
 
428  13 toggle public static void setProvider(Provider provider) throws SecurityException {
429  13 SecurityManager sm = System.getSecurityManager();
430  13 if (sm != null) {
431  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setProvider"));
432    }
433  13 setProvider0(provider);
434    }
435   
436    /**
437    * Sets the zone provider factory without performing the security check.
438    *
439    * @param provider provider to use, or null for default
440    * @throws IllegalArgumentException if the provider is invalid
441    */
 
442  15 toggle private static void setProvider0(Provider provider) {
443  15 if (provider == null) {
444  10 provider = getDefaultProvider();
445    }
446  14 Set<String> ids = provider.getAvailableIDs();
447  14 if (ids == null || ids.size() == 0) {
448  2 throw new IllegalArgumentException
449    ("The provider doesn't have any available ids");
450    }
451  12 if (!ids.contains("UTC")) {
452  1 throw new IllegalArgumentException("The provider doesn't support UTC");
453    }
454  11 if (!UTC.equals(provider.getZone("UTC"))) {
455  1 throw new IllegalArgumentException("Invalid UTC zone provided");
456    }
457  10 cProvider = provider;
458  10 cAvailableIDs = ids;
459    }
460   
461    /**
462    * Gets the default zone provider.
463    * <p>
464    * Tries the system property <code>org.joda.time.DateTimeZone.Provider</code>.
465    * Then tries a <code>ZoneInfoProvider</code> using the data in <code>org/joda/time/tz/data</code>.
466    * Then uses <code>UTCProvider</code>.
467    *
468    * @return the default name provider
469    */
 
470  10 toggle private static Provider getDefaultProvider() {
471  10 Provider provider = null;
472   
473  10 try {
474  10 String providerClass = System.getProperty("org.joda.time.DateTimeZone.Provider");
475  10 if (providerClass != null) {
476  3 try {
477  3 provider = (Provider) Class.forName(providerClass).newInstance();
478    } catch (Exception ex) {
479  1 throw new RuntimeException(ex);
480    }
481    }
482    } catch (SecurityException ex) {
483    // ignored
484    }
485   
486  9 if (provider == null) {
487  7 try {
488  7 provider = new ZoneInfoProvider("org/joda/time/tz/data");
489    } catch (Exception ex) {
490  0 ex.printStackTrace();
491    }
492    }
493   
494  9 if (provider == null) {
495  0 provider = new UTCProvider();
496    }
497   
498  9 return provider;
499    }
500   
501    //-----------------------------------------------------------------------
502    /**
503    * Gets the name provider factory.
504    * <p>
505    * The name provider is a pluggable instance factory that supplies the
506    * names of each DateTimeZone.
507    *
508    * @return the provider
509    */
 
510  14 toggle public static NameProvider getNameProvider() {
511  14 return cNameProvider;
512    }
513   
514    /**
515    * Sets the name provider factory.
516    * <p>
517    * The name provider is a pluggable instance factory that supplies the
518    * names of each DateTimeZone.
519    *
520    * @param nameProvider provider to use, or null for default
521    * @throws SecurityException if you do not have the permission DateTimeZone.setNameProvider
522    * @throws IllegalArgumentException if the provider is invalid
523    */
 
524  5 toggle public static void setNameProvider(NameProvider nameProvider) throws SecurityException {
525  5 SecurityManager sm = System.getSecurityManager();
526  5 if (sm != null) {
527  0 sm.checkPermission(new JodaTimePermission("DateTimeZone.setNameProvider"));
528    }
529  5 setNameProvider0(nameProvider);
530    }
531   
532    /**
533    * Sets the name provider factory without performing the security check.
534    *
535    * @param nameProvider provider to use, or null for default
536    * @throws IllegalArgumentException if the provider is invalid
537    */
 
538  7 toggle private static void setNameProvider0(NameProvider nameProvider) {
539  7 if (nameProvider == null) {
540  6 nameProvider = getDefaultNameProvider();
541    }
542  7 cNameProvider = nameProvider;
543    }
544   
545    /**
546    * Gets the default name provider.
547    * <p>
548    * Tries the system property <code>org.joda.time.DateTimeZone.NameProvider</code>.
549    * Then uses <code>DefaultNameProvider</code>.
550    *
551    * @return the default name provider
552    */
 
553  6 toggle private static NameProvider getDefaultNameProvider() {
554  6 NameProvider nameProvider = null;
555  6 try {
556  6 String providerClass = System.getProperty("org.joda.time.DateTimeZone.NameProvider");
557  6 if (providerClass != null) {
558  1 try {
559  1 nameProvider = (NameProvider) Class.forName(providerClass).newInstance();
560    } catch (Exception ex) {
561  0 throw new RuntimeException(ex);
562    }
563    }
564    } catch (SecurityException ex) {
565    // ignore
566    }
567   
568  6 if (nameProvider == null) {
569  5 nameProvider = new DefaultNameProvider();
570    }
571   
572  6 return nameProvider;
573    }
574   
575    //-----------------------------------------------------------------------
576    /**
577    * Converts an old style id to a new style id.
578    *
579    * @param id the old style id
580    * @return the new style id, null if not found
581    */
 
582  81 toggle private static synchronized String getConvertedId(String id) {
583  81 Map<String, String> map = cZoneIdConversion;
584  81 if (map == null) {
585    // Backwards compatibility with TimeZone.
586  2 map = new HashMap<String, String>();
587  2 map.put("GMT", "UTC");
588  2 map.put("WET", "WET");
589  2 map.put("CET", "CET");
590  2 map.put("MET", "CET");
591  2 map.put("ECT", "CET");
592  2 map.put("EET", "EET");
593  2 map.put("MIT", "Pacific/Apia");
594  2 map.put("HST", "Pacific/Honolulu"); // JDK 1.1 compatible
595  2 map.put("AST", "America/Anchorage");
596  2 map.put("PST", "America/Los_Angeles");
597  2 map.put("MST", "America/Denver"); // JDK 1.1 compatible
598  2 map.put("PNT", "America/Phoenix");
599  2 map.put("CST", "America/Chicago");
600  2 map.put("EST", "America/New_York"); // JDK 1.1 compatible
601  2 map.put("IET", "America/Indiana/Indianapolis");
602  2 map.put("PRT", "America/Puerto_Rico");
603  2 map.put("CNT", "America/St_Johns");
604  2 map.put("AGT", "America/Argentina/Buenos_Aires");
605  2 map.put("BET", "America/Sao_Paulo");
606  2 map.put("ART", "Africa/Cairo");
607  2 map.put("CAT", "Africa/Harare");
608  2 map.put("EAT", "Africa/Addis_Ababa");
609  2 map.put("NET", "Asia/Yerevan");
610  2 map.put("PLT", "Asia/Karachi");
611  2 map.put("IST", "Asia/Kolkata");
612  2 map.put("BST", "Asia/Dhaka");
613  2 map.put("VST", "Asia/Ho_Chi_Minh");
614  2 map.put("CTT", "Asia/Shanghai");
615  2 map.put("JST", "Asia/Tokyo");
616  2 map.put("ACT", "Australia/Darwin");
617  2 map.put("AET", "Australia/Sydney");
618  2 map.put("SST", "Pacific/Guadalcanal");
619  2 map.put("NST", "Pacific/Auckland");
620  2 cZoneIdConversion = map;
621    }
622  81 return map.get(id);
623    }
624   
 
625  40 toggle private static int parseOffset(String str) {
626    // Can't use a real chronology if called during class
627    // initialization. Offset parser doesn't need it anyhow.
628  40 Chronology chrono = new BaseChronology() {
629    private static final long serialVersionUID = -3128740902654445468L;
 
630  40 toggle public DateTimeZone getZone() {
631  40 return null;
632    }
 
633  40 toggle public Chronology withUTC() {
634  40 return this;
635    }
 
636  0 toggle public Chronology withZone(DateTimeZone zone) {
637  0 return this;
638    }
 
639  0 toggle public String toString() {
640  0 return getClass().getName();
641    }
642    };
643  40 return -(int) offsetFormatter().withChronology(chrono).parseMillis(str);
644    }
645   
646    /**
647    * Formats a timezone offset string.
648    * <p>
649    * This method is kept separate from the formatting classes to speed and
650    * simplify startup and classloading.
651    *
652    * @param offset the offset in milliseconds
653    * @return the time zone string
654    */
 
655  119 toggle private static String printOffset(int offset) {
656  119 StringBuffer buf = new StringBuffer();
657  119 if (offset >= 0) {
658  89 buf.append('+');
659    } else {
660  30 buf.append('-');
661  30 offset = -offset;
662    }
663   
664  119 int hours = offset / DateTimeConstants.MILLIS_PER_HOUR;
665  119 FormatUtils.appendPaddedInteger(buf, hours, 2);
666  119 offset -= hours * (int) DateTimeConstants.MILLIS_PER_HOUR;
667   
668  119 int minutes = offset / DateTimeConstants.MILLIS_PER_MINUTE;
669  119 buf.append(':');
670  119 FormatUtils.appendPaddedInteger(buf, minutes, 2);
671  119 offset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
672  119 if (offset == 0) {
673  112 return buf.toString();
674    }
675   
676  7 int seconds = offset / DateTimeConstants.MILLIS_PER_SECOND;
677  7 buf.append(':');
678  7 FormatUtils.appendPaddedInteger(buf, seconds, 2);
679  7 offset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
680  7 if (offset == 0) {
681  1 return buf.toString();
682    }
683   
684  6 buf.append('.');
685  6 FormatUtils.appendPaddedInteger(buf, offset, 3);
686  6 return buf.toString();
687    }
688   
689    /**
690    * Gets a printer/parser for managing the offset id formatting.
691    *
692    * @return the formatter
693    */
 
694  40 toggle private static synchronized DateTimeFormatter offsetFormatter() {
695  40 if (cOffsetFormatter == null) {
696  1 cOffsetFormatter = new DateTimeFormatterBuilder()
697    .appendTimeZoneOffset(null, true, 2, 4)
698    .toFormatter();
699    }
700  40 return cOffsetFormatter;
701    }
702   
703    // Instance fields and methods
704    //--------------------------------------------------------------------
705   
706    private final String iID;
707   
708    /**
709    * Constructor.
710    *
711    * @param id the id to use
712    * @throws IllegalArgumentException if the id is null
713    */
 
714  2889 toggle protected DateTimeZone(String id) {
715  2889 if (id == null) {
716  1 throw new IllegalArgumentException("Id must not be null");
717    }
718  2888 iID = id;
719    }
720   
721    // Principal methods
722    //--------------------------------------------------------------------
723   
724    /**
725    * Gets the ID of this datetime zone.
726    *
727    * @return the ID of this datetime zone
728    */
 
729  70309 toggle @ToString
730    public final String getID() {
731  70309 return iID;
732    }
733   
734    /**
735    * Returns a non-localized name that is unique to this time zone. It can be
736    * combined with id to form a unique key for fetching localized names.
737    *
738    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
739    * @return name key or null if id should be used for names
740    */
741    public abstract String getNameKey(long instant);
742   
743    /**
744    * Gets the short name of this datetime zone suitable for display using
745    * the default locale.
746    * <p>
747    * If the name is not available for the locale, then this method returns a
748    * string in the format <code>[+-]hh:mm</code>.
749    *
750    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
751    * @return the human-readable short name in the default locale
752    */
 
753  1 toggle public final String getShortName(long instant) {
754  1 return getShortName(instant, null);
755    }
756   
757    /**
758    * Gets the short name of this datetime zone suitable for display using
759    * the specified locale.
760    * <p>
761    * If the name is not available for the locale, then this method returns a
762    * string in the format <code>[+-]hh:mm</code>.
763    *
764    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
765    * @param locale the locale to get the name for
766    * @return the human-readable short name in the specified locale
767    */
 
768  2 toggle public String getShortName(long instant, Locale locale) {
769  2 if (locale == null) {
770  1 locale = Locale.getDefault();
771    }
772  2 String nameKey = getNameKey(instant);
773  2 if (nameKey == null) {
774  1 return iID;
775    }
776  1 String name = cNameProvider.getShortName(locale, iID, nameKey);
777  1 if (name != null) {
778  0 return name;
779    }
780  1 return printOffset(getOffset(instant));
781    }
782   
783    /**
784    * Gets the long name of this datetime zone suitable for display using
785    * the default locale.
786    * <p>
787    * If the name is not available for the locale, then this method returns a
788    * string in the format <code>[+-]hh:mm</code>.
789    *
790    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
791    * @return the human-readable long name in the default locale
792    */
 
793  9 toggle public final String getName(long instant) {
794  9 return getName(instant, null);
795    }
796   
797    /**
798    * Gets the long name of this datetime zone suitable for display using
799    * the specified locale.
800    * <p>
801    * If the name is not available for the locale, then this method returns a
802    * string in the format <code>[+-]hh:mm</code>.
803    *
804    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the name for
805    * @param locale the locale to get the name for
806    * @return the human-readable long name in the specified locale
807    */
 
808  10 toggle public String getName(long instant, Locale locale) {
809  10 if (locale == null) {
810  9 locale = Locale.getDefault();
811    }
812  10 String nameKey = getNameKey(instant);
813  10 if (nameKey == null) {
814  1 return iID;
815    }
816  9 String name = cNameProvider.getName(locale, iID, nameKey);
817  9 if (name != null) {
818  0 return name;
819    }
820  9 return printOffset(getOffset(instant));
821    }
822   
823    /**
824    * Gets the millisecond offset to add to UTC to get local time.
825    *
826    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
827    * @return the millisecond offset to add to UTC to get local time
828    */
829    public abstract int getOffset(long instant);
830   
831    /**
832    * Gets the millisecond offset to add to UTC to get local time.
833    *
834    * @param instant instant to get the offset for, null means now
835    * @return the millisecond offset to add to UTC to get local time
836    */
 
837  6 toggle public final int getOffset(ReadableInstant instant) {
838  6 if (instant == null) {
839  2 return getOffset(DateTimeUtils.currentTimeMillis());
840    }
841  4 return getOffset(instant.getMillis());
842    }
843   
844    /**
845    * Gets the standard millisecond offset to add to UTC to get local time,
846    * when standard time is in effect.
847    *
848    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
849    * @return the millisecond offset to add to UTC to get local time
850    */
851    public abstract int getStandardOffset(long instant);
852   
853    /**
854    * Checks whether, at a particular instant, the offset is standard or not.
855    * <p>
856    * This method can be used to determine whether Summer Time (DST) applies.
857    * As a general rule, if the offset at the specified instant is standard,
858    * then either Winter time applies, or there is no Summer Time. If the
859    * instant is not standard, then Summer Time applies.
860    * <p>
861    * The implementation of the method is simply whether {@link #getOffset(long)}
862    * equals {@link #getStandardOffset(long)} at the specified instant.
863    *
864    * @param instant milliseconds from 1970-01-01T00:00:00Z to get the offset for
865    * @return true if the offset at the given instant is the standard offset
866    * @since 1.5
867    */
 
868  8 toggle public boolean isStandardOffset(long instant) {
869  8 return getOffset(instant) == getStandardOffset(instant);
870    }
871   
872    /**
873    * Gets the millisecond offset to subtract from local time to get UTC time.
874    * This offset can be used to undo adding the offset obtained by getOffset.
875    *
876    * <pre>
877    * millisLocal == millisUTC + getOffset(millisUTC)
878    * millisUTC == millisLocal - getOffsetFromLocal(millisLocal)
879    * </pre>
880    *
881    * NOTE: After calculating millisLocal, some error may be introduced. At
882    * offset transitions (due to DST or other historical changes), ranges of
883    * local times may map to different UTC times.
884    * <p>
885    * This method will return an offset suitable for calculating an instant
886    * after any DST gap. For example, consider a zone with a cutover
887    * from 01:00 to 01:59:<br />
888    * Input: 00:00 Output: 00:00<br />
889    * Input: 00:30 Output: 00:30<br />
890    * Input: 01:00 Output: 02:00<br />
891    * Input: 01:30 Output: 02:30<br />
892    * Input: 02:00 Output: 02:00<br />
893    * Input: 02:30 Output: 02:30<br />
894    * <p>
895    * During a DST overlap (where the local time is ambiguous) this method will return
896    * the earlier instant. The combination of these two rules is to always favour
897    * daylight (summer) time over standard (winter) time.
898    * <p>
899    * NOTE: Prior to v2.0, the DST overlap behaviour was not defined and varied by hemisphere.
900    * Prior to v1.5, the DST gap behaviour was also not defined.
901    *
902    * @param instantLocal the millisecond instant, relative to this time zone, to get the offset for
903    * @return the millisecond offset to subtract from local time to get UTC time
904    */
 
905  16667 toggle public int getOffsetFromLocal(long instantLocal) {
906    // get the offset at instantLocal (first estimate)
907  16667 final int offsetLocal = getOffset(instantLocal);
908    // adjust instantLocal using the estimate and recalc the offset
909  16667 final long instantAdjusted = instantLocal - offsetLocal;
910  16667 final int offsetAdjusted = getOffset(instantAdjusted);
911    // if the offsets differ, we must be near a DST boundary
912  16667 if (offsetLocal != offsetAdjusted) {
913    // we need to ensure that time is always after the DST gap
914    // this happens naturally for positive offsets, but not for negative
915  84 if ((offsetLocal - offsetAdjusted) < 0) {
916    // if we just return offsetAdjusted then the time is pushed
917    // back before the transition, whereas it should be
918    // on or after the transition
919  43 long nextLocal = nextTransition(instantAdjusted);
920  43 long nextAdjusted = nextTransition(instantLocal - offsetAdjusted);
921  43 if (nextLocal != nextAdjusted) {
922  15 return offsetLocal;
923    }
924    }
925  16583 } else if (offsetLocal >= 0) {
926  1745 long prev = previousTransition(instantAdjusted);
927  1745 if (prev < instantAdjusted) {
928  1745 int offsetPrev = getOffset(prev);
929  1745 int diff = offsetPrev - offsetLocal;
930  1745 if (instantAdjusted - prev <= diff) {
931  100 return offsetPrev;
932    }
933    }
934    }
935  16552 return offsetAdjusted;
936    }
937   
938    /**
939    * Converts a standard UTC instant to a local instant with the same
940    * local time. This conversion is used before performing a calculation
941    * so that the calculation can be done using a simple local zone.
942    *
943    * @param instantUTC the UTC instant to convert to local
944    * @return the local instant with the same local time
945    * @throws ArithmeticException if the result overflows a long
946    * @since 1.5
947    */
 
948  154109 toggle public long convertUTCToLocal(long instantUTC) {
949  154109 int offset = getOffset(instantUTC);
950  154109 long instantLocal = instantUTC + offset;
951    // If there is a sign change, but the two values have the same sign...
952  154109 if ((instantUTC ^ instantLocal) < 0 && (instantUTC ^ offset) >= 0) {
953  0 throw new ArithmeticException("Adding time zone offset caused overflow");
954    }
955  154109 return instantLocal;
956    }
957   
958    /**
959    * Converts a local instant to a standard UTC instant with the same
960    * local time attempting to use the same offset as the original.
961    * <p>
962    * This conversion is used after performing a calculation
963    * where the calculation was done using a simple local zone.
964    * Whenever possible, the same offset as the original offset will be used.
965    * This is most significant during a daylight savings overlap.
966    *
967    * @param instantLocal the local instant to convert to UTC
968    * @param strict whether the conversion should reject non-existent local times
969    * @param originalInstantUTC the original instant that the calculation is based on
970    * @return the UTC instant with the same local time,
971    * @throws ArithmeticException if the result overflows a long
972    * @throws IllegalArgumentException if the zone has no equivalent local time
973    * @since 2.0
974    */
 
975  45337 toggle public long convertLocalToUTC(long instantLocal, boolean strict, long originalInstantUTC) {
976  45337 int offsetOriginal = getOffset(originalInstantUTC);
977  45337 long instantUTC = instantLocal - offsetOriginal;
978  45337 int offsetLocalFromOriginal = getOffset(instantUTC);
979  45337 if (offsetLocalFromOriginal == offsetOriginal) {
980  45092 return instantUTC;
981    }
982  245 return convertLocalToUTC(instantLocal, strict);
983    }
984   
985    /**
986    * Converts a local instant to a standard UTC instant with the same
987    * local time. This conversion is used after performing a calculation
988    * where the calculation was done using a simple local zone.
989    *
990    * @param instantLocal the local instant to convert to UTC
991    * @param strict whether the conversion should reject non-existent local times
992    * @return the UTC instant with the same local time,
993    * @throws ArithmeticException if the result overflows a long
994    * @throws IllegalInstantException if the zone has no equivalent local time
995    * @since 1.5
996    */
 
997  269 toggle public long convertLocalToUTC(long instantLocal, boolean strict) {
998    // get the offset at instantLocal (first estimate)
999  269 int offsetLocal = getOffset(instantLocal);
1000    // adjust instantLocal using the estimate and recalc the offset
1001  269 int offset = getOffset(instantLocal - offsetLocal);
1002    // if the offsets differ, we must be near a DST boundary
1003  269 if (offsetLocal != offset) {
1004    // if strict then always check if in DST gap
1005    // otherwise only check if zone in Western hemisphere (as the
1006    // value of offset is already correct for Eastern hemisphere)
1007  31 if (strict || offsetLocal < 0) {
1008    // determine if we are in the DST gap
1009  14 long nextLocal = nextTransition(instantLocal - offsetLocal);
1010  14 if (nextLocal == (instantLocal - offsetLocal)) {
1011  0 nextLocal = Long.MAX_VALUE;
1012    }
1013  14 long nextAdjusted = nextTransition(instantLocal - offset);
1014  14 if (nextAdjusted == (instantLocal - offset)) {
1015  0 nextAdjusted = Long.MAX_VALUE;
1016    }
1017  14 if (nextLocal != nextAdjusted) {
1018    // yes we are in the DST gap
1019  13 if (strict) {
1020    // DST gap is not acceptable
1021  0 throw new IllegalInstantException(instantLocal, getID());
1022    } else {
1023    // DST gap is acceptable, but for the Western hemisphere
1024    // the offset is wrong and will result in local times
1025    // before the cutover so use the offsetLocal instead
1026  13 offset = offsetLocal;
1027    }
1028    }
1029    }
1030    }
1031    // check for overflow
1032  269 long instantUTC = instantLocal - offset;
1033    // If there is a sign change, but the two values have different signs...
1034  269 if ((instantLocal ^ instantUTC) < 0 && (instantLocal ^ offset) < 0) {
1035  0 throw new ArithmeticException("Subtracting time zone offset caused overflow");
1036    }
1037  269 return instantUTC;
1038    }
1039   
1040    /**
1041    * Gets the millisecond instant in another zone keeping the same local time.
1042    * <p>
1043    * The conversion is performed by converting the specified UTC millis to local
1044    * millis in this zone, then converting back to UTC millis in the new zone.
1045    *
1046    * @param newZone the new zone, null means default
1047    * @param oldInstant the UTC millisecond instant to convert
1048    * @return the UTC millisecond instant with the same local time in the new zone
1049    */
 
1050  30586 toggle public long getMillisKeepLocal(DateTimeZone newZone, long oldInstant) {
1051  30586 if (newZone == null) {
1052  1 newZone = DateTimeZone.getDefault();
1053    }
1054  30586 if (newZone == this) {
1055  30347 return oldInstant;
1056    }
1057  239 long instantLocal = convertUTCToLocal(oldInstant);
1058  239 return newZone.convertLocalToUTC(instantLocal, false, oldInstant);
1059    }
1060   
1061    // //-----------------------------------------------------------------------
1062    // /**
1063    // * Checks if the given {@link LocalDateTime} is within an overlap.
1064    // * <p>
1065    // * When switching from Daylight Savings Time to standard time there is
1066    // * typically an overlap where the same clock hour occurs twice. This
1067    // * method identifies whether the local datetime refers to such an overlap.
1068    // *
1069    // * @param localDateTime the time to check, not null
1070    // * @return true if the given datetime refers to an overlap
1071    // */
1072    // public boolean isLocalDateTimeOverlap(LocalDateTime localDateTime) {
1073    // if (isFixed()) {
1074    // return false;
1075    // }
1076    // long instantLocal = localDateTime.toDateTime(DateTimeZone.UTC).getMillis();
1077    // // get the offset at instantLocal (first estimate)
1078    // int offsetLocal = getOffset(instantLocal);
1079    // // adjust instantLocal using the estimate and recalc the offset
1080    // int offset = getOffset(instantLocal - offsetLocal);
1081    // // if the offsets differ, we must be near a DST boundary
1082    // if (offsetLocal != offset) {
1083    // long nextLocal = nextTransition(instantLocal - offsetLocal);
1084    // long nextAdjusted = nextTransition(instantLocal - offset);
1085    // if (nextLocal != nextAdjusted) {
1086    // // in DST gap
1087    // return false;
1088    // }
1089    // long diff = Math.abs(offset - offsetLocal);
1090    // DateTime dateTime = localDateTime.toDateTime(this);
1091    // DateTime adjusted = dateTime.plus(diff);
1092    // if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1093    // dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1094    // dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1095    // return true;
1096    // }
1097    // adjusted = dateTime.minus(diff);
1098    // if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1099    // dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1100    // dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1101    // return true;
1102    // }
1103    // return false;
1104    // }
1105    // return false;
1106    // }
1107    //
1108    //
1109    // DateTime dateTime = null;
1110    // try {
1111    // dateTime = localDateTime.toDateTime(this);
1112    // } catch (IllegalArgumentException ex) {
1113    // return false; // it is a gap, not an overlap
1114    // }
1115    // long offset1 = Math.abs(getOffset(dateTime.getMillis() + 1) - getStandardOffset(dateTime.getMillis() + 1));
1116    // long offset2 = Math.abs(getOffset(dateTime.getMillis() - 1) - getStandardOffset(dateTime.getMillis() - 1));
1117    // long offset = Math.max(offset1, offset2);
1118    // if (offset == 0) {
1119    // return false;
1120    // }
1121    // DateTime adjusted = dateTime.plus(offset);
1122    // if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1123    // dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1124    // dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1125    // return true;
1126    // }
1127    // adjusted = dateTime.minus(offset);
1128    // if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1129    // dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1130    // dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1131    // return true;
1132    // }
1133    // return false;
1134   
1135    // long millis = dateTime.getMillis();
1136    // long nextTransition = nextTransition(millis);
1137    // long previousTransition = previousTransition(millis);
1138    // long deltaToPreviousTransition = millis - previousTransition;
1139    // long deltaToNextTransition = nextTransition - millis;
1140    // if (deltaToNextTransition < deltaToPreviousTransition) {
1141    // int offset = getOffset(nextTransition);
1142    // int standardOffset = getStandardOffset(nextTransition);
1143    // if (Math.abs(offset - standardOffset) >= deltaToNextTransition) {
1144    // return true;
1145    // }
1146    // } else {
1147    // int offset = getOffset(previousTransition);
1148    // int standardOffset = getStandardOffset(previousTransition);
1149    // if (Math.abs(offset - standardOffset) >= deltaToPreviousTransition) {
1150    // return true;
1151    // }
1152    // }
1153    // return false;
1154    // }
1155   
1156    /**
1157    * Checks if the given {@link LocalDateTime} is within a gap.
1158    * <p>
1159    * When switching from standard time to Daylight Savings Time there is
1160    * typically a gap where a clock hour is missing. This method identifies
1161    * whether the local datetime refers to such a gap.
1162    *
1163    * @param localDateTime the time to check, not null
1164    * @return true if the given datetime refers to a gap
1165    * @since 1.6
1166    */
 
1167  22 toggle public boolean isLocalDateTimeGap(LocalDateTime localDateTime) {
1168  22 if (isFixed()) {
1169  0 return false;
1170    }
1171  22 try {
1172  22 localDateTime.toDateTime(this);
1173  16 return false;
1174    } catch (IllegalInstantException ex) {
1175  6 return true;
1176    }
1177    }
1178   
1179    /**
1180    * Adjusts the offset to be the earlier or later one during an overlap.
1181    *
1182    * @param instant the instant to adjust
1183    * @param earlierOrLater false for earlier, true for later
1184    * @return the adjusted instant millis
1185    */
 
1186  18 toggle public long adjustOffset(long instant, boolean earlierOrLater) {
1187    // a bit messy, but will work in all non-pathological cases
1188   
1189    // evaluate 3 hours before and after to work out if anything is happening
1190  18 long instantBefore = instant - 3 * DateTimeConstants.MILLIS_PER_HOUR;
1191  18 long instantAfter = instant + 3 * DateTimeConstants.MILLIS_PER_HOUR;
1192  18 long offsetBefore = getOffset(instantBefore);
1193  18 long offsetAfter = getOffset(instantAfter);
1194  18 if (offsetBefore <= offsetAfter) {
1195  6 return instant; // not an overlap (less than is a gap, equal is normal case)
1196    }
1197   
1198    // work out range of instants that have duplicate local times
1199  12 long diff = offsetBefore - offsetAfter;
1200  12 long transition = nextTransition(instantBefore);
1201  12 long overlapStart = transition - diff;
1202  12 long overlapEnd = transition + diff;
1203  12 if (instant < overlapStart || instant >= overlapEnd) {
1204  4 return instant; // not an overlap
1205    }
1206   
1207    // calculate result
1208  8 long afterStart = instant - overlapStart;
1209  8 if (afterStart >= diff) {
1210    // currently in later offset
1211  4 return earlierOrLater ? instant : instant - diff;
1212    } else {
1213    // currently in earlier offset
1214  4 return earlierOrLater ? instant + diff : instant;
1215    }
1216    }
1217    // System.out.println(new DateTime(transitionStart, DateTimeZone.UTC) + " " + new DateTime(transitionStart, this));
1218   
1219    //-----------------------------------------------------------------------
1220    /**
1221    * Returns true if this time zone has no transitions.
1222    *
1223    * @return true if no transitions
1224    */
1225    public abstract boolean isFixed();
1226   
1227    /**
1228    * Advances the given instant to where the time zone offset or name changes.
1229    * If the instant returned is exactly the same as passed in, then
1230    * no changes occur after the given instant.
1231    *
1232    * @param instant milliseconds from 1970-01-01T00:00:00Z
1233    * @return milliseconds from 1970-01-01T00:00:00Z
1234    */
1235    public abstract long nextTransition(long instant);
1236   
1237    /**
1238    * Retreats the given instant to where the time zone offset or name changes.
1239    * If the instant returned is exactly the same as passed in, then
1240    * no changes occur before the given instant.
1241    *
1242    * @param instant milliseconds from 1970-01-01T00:00:00Z
1243    * @return milliseconds from 1970-01-01T00:00:00Z
1244    */
1245    public abstract long previousTransition(long instant);
1246   
1247    // Basic methods
1248    //--------------------------------------------------------------------
1249   
1250    /**
1251    * Get the datetime zone as a {@link java.util.TimeZone}.
1252    *
1253    * @return the closest matching TimeZone object
1254    */
 
1255  587 toggle public java.util.TimeZone toTimeZone() {
1256  587 return java.util.TimeZone.getTimeZone(iID);
1257    }
1258   
1259    /**
1260    * Compare this datetime zone with another.
1261    *
1262    * @param object the object to compare with
1263    * @return true if equal, based on the ID and all internal rules
1264    */
1265    public abstract boolean equals(Object object);
1266   
1267    /**
1268    * Gets a hash code compatable with equals.
1269    *
1270    * @return suitable hashcode
1271    */
 
1272  2712 toggle public int hashCode() {
1273  2712 return 57 + getID().hashCode();
1274    }
1275   
1276    /**
1277    * Gets the datetime zone as a string, which is simply its ID.
1278    * @return the id of the zone
1279    */
 
1280  4 toggle public String toString() {
1281  4 return getID();
1282    }
1283   
1284    /**
1285    * By default, when DateTimeZones are serialized, only a "stub" object
1286    * referring to the id is written out. When the stub is read in, it
1287    * replaces itself with a DateTimeZone object.
1288    * @return a stub object to go in the stream
1289    */
 
1290  28 toggle protected Object writeReplace() throws ObjectStreamException {
1291  28 return new Stub(iID);
1292    }
1293   
1294    /**
1295    * Used to serialize DateTimeZones by id.
1296    */
 
1297    private static final class Stub implements Serializable {
1298    /** Serialization lock. */
1299    private static final long serialVersionUID = -6471952376487863581L;
1300    /** The ID of the zone. */
1301    private transient String iID;
1302   
1303    /**
1304    * Constructor.
1305    * @param id the id of the zone
1306    */
 
1307  28 toggle Stub(String id) {
1308  28 iID = id;
1309    }
1310   
 
1311  28 toggle private void writeObject(ObjectOutputStream out) throws IOException {
1312  28 out.writeUTF(iID);
1313    }
1314   
 
1315  48 toggle private void readObject(ObjectInputStream in) throws IOException {
1316  48 iID = in.readUTF();
1317    }
1318   
 
1319  48 toggle private Object readResolve() throws ObjectStreamException {
1320  48 return forID(iID);
1321    }
1322    }
1323   
1324    }